﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using JoyServer.Events;
using JoyServer.Input;

namespace JoyServer
{
	/// <summary>
	/// ジョイスティックの設定画面と、
	/// ログ表示画面を出すためのフォーム
	/// </summary>
	public partial class FormConfig : Form
	{
		/// <summary>
		/// デバイスが 1 つ以上ある場合 true
		/// デバイスが 1 つも無い場合 false
		/// </summary>
		private bool detected;

		/// <summary>
		/// コンストラクタ
		/// </summary>
		public FormConfig()
		{
			InitializeComponent();
		}

		/// <summary>
		/// デバイス一覧を更新
		/// </summary>
		private void refreshDeviceList()
		{
			detected = false;

			// グローバル変数の領域にある input オブジェクトから
			// デバイスの名前一覧を取得する
			List<String> names = Global.input.getDeviceNames();
			int number = 0;

			// コンボボックスにデバイスの名前一覧を追加
			comboBoxDeviceList.Items.Clear();
			foreach (String name in names)
			{
				comboBoxDeviceList.Items.Add(number.ToString() + ": " + name);
				number++;
			}

			// デバイスが 1 つ以上見つかった時は
			// ポーリング用タイマーを開始する
			if (comboBoxDeviceList.Items.Count > 0)
			{
				detected = true;
				timerRefresh.Start();
				comboBoxDeviceList.SelectedIndex = 0;
			}
		}

		/// <summary>
		/// リストボックスにログをロードする
		/// </summary>
		/// <param name="log"></param>
		private void loadLog(List<String> log)
		{
			if (log == null)
				return;

			listBoxLog.Items.Clear();
			foreach (String line in log)
			{
				listBoxLog.Items.Add(line);
			}
		}

		/// <summary>
		/// ログが追加された時に実行されるイベント
		/// </summary>
		/// <param name="e"></param>
		private void onLogAdded(Event e)
		{
			// GUI 用のスレッドと、このイベントの呼び出し元スレッドが
			// 違うスレッドで動いているので、エラー回避のために
			// イベントを GUI 用スレッドで呼び出し直す
			if (this.InvokeRequired)
			{
				EventFunction function;
				function = new EventFunction(onLogAdded);
				try
				{
					this.Invoke(function, new object[] { e });
				}
				catch (InvalidOperationException)
				{
					return;
				}
				return;
			}

			// すでに開放されている時はそのまま終了
			if (listBoxLog.IsDisposed)
				return;

			// イベントオブジェクトの取得
			LoggerEvent loggerEvent = e as LoggerEvent;
			if (loggerEvent == null)
				return;

			// ログを 1 行取得してリストボックスに追加
			String line = loggerEvent.line;
			listBoxLog.Items.Insert(0, line);

			if (listBoxLog.Items.Count > Logger.LOG_MAX)
			{
				listBoxLog.Items.RemoveAt(listBoxLog.Items.Count - 1);
			}
		}

		/// <summary>
		/// フォームがロードされた時
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void FormConfig_Load(object sender, EventArgs e)
		{
			// デバイス一覧を表示するコンボボックスを更新
			refreshDeviceList();

			// 現在のログを読み込み
			List<String> log = Global.logger.read();
			loadLog(log);

			// ログ更新用イベントハンドラを追加
			Global.logger.addEventListener(LoggerEvent.ADDED, onLogAdded);
		}

		/// <summary>
		/// ボタンの状態をポーリングするタイマーが
		/// 指定時間に達した時
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void timerRefresh_Tick(object sender, EventArgs e)
		{
			// デバイスが 1 つも見つからなかった場合そのまま終了
			if (detected == false)
				return;
			if (Global.input == null)
				return;

			// 読み込むデバイスの番号を取得
			int number = comboBoxDeviceList.SelectedIndex;

			if (number < 0)
				return;

			// 指定されたジョイスティックデバイスの現在の状態を取得する
			Joystick joystick = Global.input.read((uint)number);

			if (joystick == null)
				return;

			// GUI の状態を更新する
			checkBoxButton0.Checked = (joystick.buttons[0] > 0);
			checkBoxButton1.Checked = (joystick.buttons[1] > 0);
			checkBoxButton2.Checked = (joystick.buttons[2] > 0);
			checkBoxButton3.Checked = (joystick.buttons[3] > 0);
			checkBoxButton4.Checked = (joystick.buttons[4] > 0);
			checkBoxButton5.Checked = (joystick.buttons[5] > 0);
			checkBoxButton6.Checked = (joystick.buttons[6] > 0);
			checkBoxButton7.Checked = (joystick.buttons[7] > 0);
			checkBoxButton8.Checked = (joystick.buttons[8] > 0);
			checkBoxButton9.Checked = (joystick.buttons[9] > 0);

			if (joystick.x > trackBarLR.Maximum)
			{
				trackBarLR.Value = trackBarLR.Maximum;
			}
			else if (joystick.x < trackBarLR.Minimum)
			{
				trackBarLR.Value = trackBarLR.Minimum;
			}
			else
			{
				trackBarLR.Value = joystick.x;
			}

			if (joystick.y > trackBarUD.Maximum)
			{
				trackBarUD.Value = trackBarUD.Maximum;
			}
			else if (joystick.y < trackBarUD.Minimum)
			{
				trackBarUD.Value = trackBarUD.Minimum;
			}
			else
			{
				trackBarUD.Value = joystick.y;
			}

			textBoxLR.Text = joystick.x.ToString();
			textBoxUD.Text = joystick.y.ToString();
		}

		/// <summary>
		/// フォームが閉じられる時
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void FormConfig_FormClosing(object sender, FormClosingEventArgs e)
		{
			timerRefresh.Stop();
			Global.logger.removeEventListener(LoggerEvent.ADDED, onLogAdded);
		}

		/// <summary>
		/// 「設定画面を閉じる」ボタンがクリックされた時
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void buttonClose_Click(object sender, EventArgs e)
		{
			Close();
		}

		/// <summary>
		/// 「デバイスリストの更新」ボタンがクリックされた時
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void buttonRefresh_Click(object sender, EventArgs e)
		{
			Global.input.resresh();
			refreshDeviceList();
		}

	}
}
